package multimanipulators;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import be.djdb.Statics;


import multimanipulators.interfaces.Converters;
import multimanipulators.interfaces.Finders;
/**
 * @author Lieven Roegiers
 * @copyright 2009
 * @project javamylibs
 * @from http://javamylibs.googlecode.com/svn/trunk/
**/
public class StreamMultiManipulatorALFA implements Serializable {
	private int maxbuffer=0;//wil zeggen wat de grootste lock is van alle finders
	private static final boolean DEBUG = false;
	public Map<Finders,Converters>transformerslist;
	public List<Finders>finders;
	private Finders finder;
	private int charint;//actieve char 
	private boolean ismatch;//wil zeggen wanneer er een positieve match is gevonden
	private int bufferclock;
	private char buffer[]={'a'};
	private int buffersizeSequence;
	public  StreamMultiManipulatorALFA (int buffersizeSequence){
		if (buffersizeSequence>0){
			this.buffersizeSequence=buffersizeSequence;
		}else{
			this.buffersizeSequence=20;
		}
	}
	private void init(){
		buffer= new char[this.buffersizeSequence];
        ismatch = false;
        bufferclock =0;
        for (Finders finder : finders){
        	finder.init();
        }
    }
	public void findAndReplace(InputStream input,Map<Finders,Converters>map,OutputStreamWriter Doutstream) throws IOException{
		if (DEBUG){	Doutstream.append("\n<<DEBUGMODE>>");
					Doutstream.append("\n<DEBUGtag:null(do nothing)>//loss buffer");
					Doutstream.append("\n<DEBUGtag:resize buffer(resize the buffer and append to stream)>//match not found buffer bigger than must to used finder");
					Doutstream.append("\n<DEBUGtag:found (one of the finders has a match wil stop al other start replace de finders wil start after the replace)>//search for match is found");
		}
		if (DEBUG){Doutstream.append("\n <<founed char>><MAX:'min open bufferlength',I:'actual length buffer'>");}
		transformerslist = map;
	    finders = new ArrayList<Finders>();
	    init();
        for (Finders finder : transformerslist.keySet()) {
        	finders.add(finder);
        }
		try{
	    while ((charint = input.read()) != -1 ){
    		if (DEBUG){  
    			if(Level.FINE != null){ Doutstream.append("\n <<"+(char)charint+">><MAX:"+maxbuffer+",I:"+bufferclock+">");
    			}else{Doutstream.append("'"+(char)charint+"'");}			
    		}
			if (finderobjects(charint)){
				if (DEBUG){Doutstream.append(finder.toString());}
				    if (maxbuffer<bufferclock ){
				    	if (DEBUG){Doutstream.append("<DEBUGtag:append and ResizeTo:'"+maxbuffer+"'>");}
				    	Doutstream.append(new String(trimArray(buffer,0,bufferclock - maxbuffer)));
				    }
					Converters x =map.get(finder);
		    		try {
						x.manipulate(trimArray(buffer,0,0),Doutstream);
					}catch (IOException e) {
						//log()
						//throw new RuntimeException("IOExetion:=>"+x.getname()+"=>\n"+ e.getMessage());
					}
	    		init();
	    	}else if(maxbuffer<bufferclock ){if (DEBUG){Doutstream.append("<DEBUGtag:append and ResizeTo:'"+maxbuffer+"'>");}
	    		Doutstream.append(new String(trimArray(buffer,0,bufferclock - maxbuffer)));
	    		buffer = trimArray(buffer, bufferclock, buffer.length - bufferclock - maxbuffer);
	    		bufferclock =maxbuffer;
	    	}else if(maxbuffer==0 ){if (DEBUG){Doutstream.append("\n<DEBUGtag:null>");}
	    		Doutstream.append(new String(trimArray(buffer,0,0)));
	        }
	    }
		}catch(StringIndexOutOfBoundsException e){
			System.out.println("ERROR OUT OF BOUND");			
		}catch (Exception e){
			log(Level.FINE,e.getMessage());
			if (DEBUG){e.printStackTrace();}
		} 
	}
	private char[] trimArray(char [] input,int start,int length){
		char result[] = null;
		int mlength = 0;
		if (length>0){
			mlength = length;
		}else if(false!= false){
		}else{
			for (char value :input){
				if(value>0){
					mlength++;
				}
			}
		}
		try{
			result = new char[mlength];
		    System.arraycopy(input, start, result,0, mlength);
		}catch (ArrayIndexOutOfBoundsException e) {
			if (DEBUG){System.out.print(start + " lenfth:" + mlength +"maxlen:" + input.length ); 
			System.out.println( input); }
		}
		return result;
	}
	private void make_buffer_bigger(){
		char buffere[] = new char[buffer.length + buffersizeSequence];
		System.arraycopy(buffer, 0, buffere, 0, buffer.length);//arraycopy(Object srcarray, int srcPos,Object destarray,int destPos,int length)
		buffer = buffere;
	}
	private boolean finderobjects(int achar){	
		boolean ischarfound = false;
		for (Iterator<Finders> iter = finders.iterator(); iter.hasNext();) {//Generics No casting =>faster
            finder = iter.next();
            
            if(finder.isnext(achar)){//equals char
                ischarfound =true;
            }
            if(finder.ismatchfound()){
            	ismatch = finder.ismatchfound();
    			buffer[bufferclock]=(char)achar;
            	maxbuffer =0;
            	return true;
            }
        }
		try{
			buffer[bufferclock]=(char)achar;
		}catch (ArrayIndexOutOfBoundsException e) {
			make_buffer_bigger();
			buffer[bufferclock]=(char)achar;
		}
		if (ischarfound){//it means that in one or more the a piece match was
			bufferclock++;
            if (maxbuffer<bufferclock){maxbuffer = bufferclock;}
		}else{//no finder has found a piece of a match
			maxbuffer = 0;
		}
		return false;
	}
    private static  void log(Level level, String msg ){
        String tag ="<>>>>*"+Statics.LIBNAME+"-"+Statics.COPYRIGHTBY+"*<<<<>";
        Logger.getLogger(StreamMultiManipulator.class.getName()).log(level, tag + msg);
    }
}